#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "obd_pid.h"
#include "util.h"

int
valid_config(struct config *conf)
{
	uint8_t *p;
	uint16_t crc, orig_crc;
	int i;

	p = (uint8_t *) conf;
	orig_crc = conf->crc;

	conf->crc = 0;
	crc = 0xffff;
	for (i = 0; i < sizeof(*conf); ++i) {
		crc = crc16_update(crc, *p);
		++p;
	}

	conf->crc = orig_crc;

	if (crc == orig_crc)
		return 1;
	
	return 0;
}

int
valid_crc(void *vbuf, int len)
{
	uint8_t *p;
	uint16_t crc, orig_crc;
	int i;

	p = (uint8_t *) vbuf;
	memcpy(&orig_crc, p + len - sizeof(crc), sizeof(crc));
	memset(p + len - sizeof(crc), 0, sizeof(crc));

	crc = 0xffff;
	for (i = 0; i < len; ++i)
		crc = crc16_update(crc, p[i]);

	if (crc == orig_crc)
		return 1;
	
	return 0;
}

uint16_t
crc16_update(uint16_t crc, uint8_t a)
{
	int i;

	crc ^= a;
	for (i = 0; i < 8; ++i) {
		if (crc & 1)
			crc = (crc >> 1) ^ 0xA001;
		else
			crc = (crc >> 1);
	}

	return crc;
}

void
dump_config(struct config *conf)
{
	int i;
	int count;

	count = 0;
	for (i = 0; i < NUM_LOG_SLOTS; ++i) {
		if (conf->event[i].interval > 0) {
			printf("Event %d:\n", count++);
			printf("\tInterval: %d (%g seconds)\n",
			    conf->event[i].interval,
			    conf->event[i].interval / 2.0);
			printf("\tCount: %d\n", conf->event[i].count);
			printf("\tAction: %s\n",conf->event[i].action ?
			    "ACTION_FLASH" : "ACTION_ELM");
			printf("\tMode: %02x\n", conf->event[i].mode);

			if (conf->event[i].mode == 1) {
				printf("\tPID: %s (0x%02x)\n",
				    pid_name[conf->event[i].pid],
				    conf->event[i].pid);
			} else {
				printf("\tPID: 0x%02x\n",
				    conf->event[i].pid);
			}
		}
	}

	printf("NVM Start Address: %08x\n", conf->nvm_start_addr);
}

char *
pid_units(uint8_t pid)
{
	switch (pid) {
	case ENGINE_LOAD:
	case SHORT_TERM_FUEL_TRIM_B1:
	case LONG_TERM_FUEL_TRIM_B1:
	case SHORT_TERM_FUEL_TRIM_B2:
	case LONG_TERM_FUEL_TRIM_B2:
	case THROTTLE_POS:
	case FUEL_LEVEL:
	case FUEL_ETH_PER:
		return "%";
		break;

	case ENGINE_TEMP:
	case INTAKE_TEMP:
		return "deg C";
		break;

	case PRESSURE_FUEL:
	case PRESSURE_MANIFOLD:
	case PRESS_FUELRAIL:
		return "kPa";
		break;

	case ENGINE_RPM:
		return "RPM";
		break;

	case VEHICLE_SPEED:
		return "mph";
		break;

	case TIMING_ADVANCE:
		return "deg relative to #1 cylinder";
		break;

	case MAF_AIRFLOW:
		return "g/s";
		break;

	case ENGINE_RUNTIME:
		return "sec";
		break;

	default:
		return "RAW";
		break;
	}
}

double
conv_pid_data(uint8_t pid, uint32_t data)
{
	double val;

	switch (pid) {
	case ENGINE_LOAD:
		val = CONV_ENGINE_LOAD(data);
		break;

	case ENGINE_TEMP:
		val = CONV_ENGINE_TEMP(data);
		break;

	case SHORT_TERM_FUEL_TRIM_B1:
		val = CONV_SHORT_TERM_FUEL_TRIM_B1(data);
		break;

	case LONG_TERM_FUEL_TRIM_B1:
		val = CONV_LONG_TERM_FUEL_TRIM_B1(data);
		break;

	case SHORT_TERM_FUEL_TRIM_B2:
		val = CONV_SHORT_TERM_FUEL_TRIM_B2(data);
		break;

	case LONG_TERM_FUEL_TRIM_B2:
		val = CONV_LONG_TERM_FUEL_TRIM_B2(data);
		break;

	case PRESSURE_FUEL:
		val = CONV_PRESSURE_FUEL(data);
		break;

	case PRESSURE_MANIFOLD:
		val = CONV_PRESSURE_MANIFOLD(data);
		break;

	case ENGINE_RPM:
		val = CONV_ENGINE_RPM(data);
		break;

	case VEHICLE_SPEED:
		val = CONV_VEHICLE_SPEED(data) * 0.621371192;
		break;

	case TIMING_ADVANCE:
		val = CONV_TIMING_ADVANCE(data);
		break;

	case INTAKE_TEMP:
		val = CONV_INTAKE_TEMP(data);
		break;

	case MAF_AIRFLOW:
		val = CONV_MAF_AIRFLOW(data);
		break;

	case THROTTLE_POS:
		val = CONV_THROTTLE_POS(data);
		break;

	case ENGINE_RUNTIME:
		val = CONV_ENGINE_RUNTIME(data);
		break;

	case PRESS_FUELRAIL:
		val = CONV_PRESS_FUELRAIL(data);
		break;

	case PRESS_FUELDIESEL:
		val = CONV_PRESS_FUELDIESEL(data);
		break;

	case FUEL_LEVEL:
		val = CONV_FUEL_LEVEL(data);
		break;

	case FUEL_ETH_PER:
		val = CONV_FUEL_ETH_PER(data);
		break;
	}

	return val;
}
